package com.lry.rpc.starter.extend;

import com.lry.rpc.config.NettyClientConfig;
import com.lry.rpc.config.NettyServerConfig;
import com.lry.rpc.config.RpcReferenceBean;
import com.lry.rpc.config.RpcServiceBean;
import com.lry.rpc.netty.client.RpcClient;
import com.lry.rpc.netty.server.RpcServer;
import com.lry.rpc.starter.bean.ReferenceBean;
import com.lry.rpc.starter.bean.ServiceBean;
import com.lry.rpc.starter.config.*;
import com.lry.util.CollectionUtils;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.impl.MapperFacadeImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.SmartLifecycle;

import java.util.Map;
import java.util.concurrent.CompletableFuture;
//todo group merger分组聚合实现，javasist，asm代理接入，注册中心接入redis，nacos，实现配置中心等，metrics收集控制台，熔断限流降级
public class RpcStartupLifecycle implements SmartLifecycle, ApplicationContextAware {

    private ApplicationContext applicationContext;

    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    MapperFacade mapperFacade = mapperFactory.getMapperFacade();
    @Override
    public void start() {
        ApplicationConfig applicationConfig = BeanFactoryUtils.beanOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class);
        RegistryConfig registryConfig = BeanFactoryUtils.beanOfTypeIncludingAncestors(applicationContext, RegistryConfig.class);
        TransportConfig transportConfig = BeanFactoryUtils.beanOfTypeIncludingAncestors(applicationContext, TransportConfig.class);

        startServer(applicationConfig,registryConfig,transportConfig);
    }

    private void startClient(ApplicationConfig applicationConfig, RegistryConfig registryConfig, TransportConfig transportConfig) {
        Map<String, ReferenceBean> referenceBeanMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ReferenceBean.class);
        if(CollectionUtils.isEmpty(referenceBeanMap)){
            return;
        }
        ConsumerConfig consumerConfig = BeanFactoryUtils.beanOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class);

        NettyClientConfig nettyClientConfig = new NettyClientConfig();
        nettyClientConfig.setApplicationName(applicationConfig.getName());
        nettyClientConfig.setRegistryAddress(registryConfig.getAddress());

        RpcClient rpcClient = new RpcClient(nettyClientConfig);
        for (ReferenceBean referenceBean : referenceBeanMap.values()) {

            referenceBean.setToken(consumerConfig.getToken());
            referenceBean.setCompressor(transportConfig.getCompressor());
            referenceBean.setSerializer(transportConfig.getSerializer());

            RpcReferenceBean rpcReferenceBean = RpcReferenceBean.builder().
                    version(referenceBean.getVersion()).group(referenceBean.getGroup()).
                    merger(referenceBean.getMerger()).timeout(referenceBean.getTimeout()).
                    token(referenceBean.getToken()).url(referenceBean.getUrl()).
                    retries(referenceBean.getRetries()).
                    filter(referenceBean.getFilter()).serializer(referenceBean.getSerializer()).
                    serviceImplName(referenceBean.getServiceImplName()).
                    compressor(referenceBean.getCompressor()).interfaceClass(referenceBean.getInterfaceClass()).build();

            if(referenceBean.isAsync()){
                rpcReferenceBean.setInterfaceClass(referenceBean.getInterfaceClass().getInterfaces()[0]);
            }
            rpcClient.subscribeService(rpcReferenceBean);
        }
        rpcClient.start();
    }


    private void startServer(ApplicationConfig applicationConfig, RegistryConfig registryConfig, TransportConfig transportConfig) {
        Map<String, ServiceBean> serviceBeanMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ServiceBean.class);
        if(CollectionUtils.isEmpty(serviceBeanMap)){
            startClient(applicationConfig,registryConfig,transportConfig);
            return;
        }
        ProviderConfig providerConfig = BeanFactoryUtils.beanOfTypeIncludingAncestors(applicationContext, ProviderConfig.class);
        NettyServerConfig nettyServerConfig = new NettyServerConfig();
        nettyServerConfig.setApplicationName(applicationConfig.getName());
        nettyServerConfig.setRegistryAddress(registryConfig.getAddress());
        nettyServerConfig.setPort(providerConfig.getPort());

        RpcServer rpcServer = new RpcServer(nettyServerConfig);
        for (ServiceBean serviceBean : serviceBeanMap.values()) {
            serviceBean.setWeight(providerConfig.getWeight());
            serviceBean.setToken(providerConfig.getToken());

            RpcServiceBean rpcServiceBean = RpcServiceBean.builder().
                    version(serviceBean.getVersion()).group(serviceBean.getGroup()).
                    weight(serviceBean.getWeight()).ref(serviceBean.get()).interfaceClass(serviceBean.getInterfaceClass()).
                    serviceImplName(serviceBean.getServiceImplName()).
                    interfaceName(serviceBean.getInterfaceName()).token(serviceBean.getToken()).build();

            rpcServer.registerService(rpcServiceBean);
        }
        CompletableFuture.runAsync(()->{
            rpcServer.start(()-> {
                startClient(applicationConfig,registryConfig,transportConfig);
            });
        });
    }


    @Override
    public boolean isAutoStartup() {
        return true;
    }

    @Override
    public void stop() {
    }

    @Override
    public boolean isRunning() {
        return false;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
